package de.geraldschenke.tictactoe.server;

import java.util.ArrayList;
import java.util.List;

import android.util.Log;

import de.geraldschenke.tictactoe.common.GameEvent;
import de.geraldschenke.tictactoe.common.GameProperties;
import de.geraldschenke.tictactoe.common.Stone;
import de.geraldschenke.tictactoe.common.RemotePlayer;

public class Game {

	private Stone[][] mBoard;
	private int mCurrentPlayer = 0;
	private final List<RemotePlayer> mPlayers = new ArrayList<RemotePlayer>();
	private final List<RemotePlayer> mObservers = new ArrayList<RemotePlayer>();
	public boolean isRunning = false;
	
	private boolean gameOver;
	private Stone mWinner;

	public void initAndStart(GameProperties gp) {
		// TODO check for enough players!
		mBoard = new Stone[gp.boardSize][gp.boardSize];
		mCurrentPlayer = 0;
		isRunning = true;
		broadcastUpdate();
	}

	public Stone getFieldOwner(int x, int y) {
		return mBoard[x][y];
	}

	public int getBoardSize() {
		return mBoard.length;
	}

	/**
	 * Sets the owner of the field at given coordinates to current player. It is
	 * NOT checked, if this field is already taken or if the coordinates are
	 * valid.
	 * 
	 * @param x
	 * @param y
	 */
	public void makeAMove(int x, int y) {
		if (gameOver){
			return;
		}
		if (mCurrentPlayer == 0){
			mBoard[x][y] = Stone.X;
		}else{
			mBoard[x][y] = Stone.O;
		}
		mCurrentPlayer = (mCurrentPlayer+1) % mPlayers.size();
	}

	public void broadcastUpdate() {
		GameEvent gameEvent = new GameEvent();
		gameEvent.type = GameEvent.Type.UPDATE_BOARD;
		gameEvent.board = mBoard;
		gameEvent.currentPlayer = mCurrentPlayer;
		gameEvent.gameOver = gameOver;
		gameEvent.winner = mWinner;
		for(int i = 0; i < mPlayers.size(); i++){
			mPlayers.get(i).handleGameEvent(gameEvent);
		}
		for(int i = 0; i < mObservers.size(); i++){
			mObservers.get(i).handleGameEvent(gameEvent);
		}
	}

	
	private boolean isBoardFull(){
		for (int i = 0; i < mBoard.length; i++) {
			for (int j = 0; j < mBoard[i].length; j++) {
				if (mBoard[i][j] == null) {
					// still free fields available, so no draw
					return false;
				}
			}
		}
		return true;
	}
	
	/**
	 * @return the winner of the game; null if there is none (yet)
	 */
	public void validateBoard() {
		Stone winner = null;
		int winningCondition = mBoard.length;
		int xCounter = 0;
		int oCounter = 0;
		// check rows
		for (int i = 0; i < mBoard.length; i++) {
			xCounter = 0;
			oCounter = 0;
			for (int j = 0; j < mBoard[i].length; j++) {
				if (mBoard[i][j] == Stone.X) {
					xCounter++;
				}
				if (mBoard[i][j] == Stone.O) {
					oCounter++;
				}
			}
			if (xCounter == winningCondition) {
				winner = Stone.X;
			} else if (oCounter == winningCondition) {
				winner = Stone.O;
			}
		}
		// check columns
		for (int i = 0; i < mBoard.length; i++) {
			xCounter = 0;
			oCounter = 0;
			for (int j = 0; j < mBoard[i].length; j++) {
				if (mBoard[j][i] == Stone.X) {
					xCounter++;
				}
				if (mBoard[j][i] == Stone.O) {
					oCounter++;
				}
			}
			if (xCounter == winningCondition) {
				winner = Stone.X;
			} else if (oCounter == winningCondition) {
				winner = Stone.O;
			}
		}
		// check diagonals
		xCounter = 0;
		oCounter = 0;
		for (int i = 0; i < mBoard.length; i++) {
			if (mBoard[i][i] == Stone.X) {
				xCounter++;
			}
			if (mBoard[i][i] == Stone.O) {
				oCounter++;
			}
			if (xCounter == winningCondition) {
				winner = Stone.X;
			} else if (oCounter == winningCondition) {
				winner = Stone.O;
			}
		}
		xCounter = 0;
		oCounter = 0;
		for (int i = 0; i < mBoard.length; i++) {
			if (mBoard[i][(mBoard.length - 1) - i] == Stone.X) {
				xCounter++;
			}
			if (mBoard[i][(mBoard.length - 1) - i] == Stone.O) {
				oCounter++;
			}
			if (xCounter == winningCondition) {
				winner = Stone.X;
			} else if (oCounter == winningCondition) {
				winner = Stone.O;
			}
		}
		
		
		if(winner != null || isBoardFull()){
			Log.d("Game", "The game is over! Winner is: "+winner);
			gameOver = true;
			mWinner = winner;
		}
	}

	public boolean isBoardEmpty() {
		for (int i = 0; i < mBoard.length; i++) {
			for (int j = 0; j < mBoard[i].length; j++) {
				if (mBoard[i][j] != null) {
					return false;
				}
			}
		}
		return true;
	}

	private void clearBoard() {
		for (int i = 0; i < mBoard.length; i++) {
			for (int j = 0; j < mBoard[i].length; j++) {
				mBoard[i][j] = null;
			}
		}
		gameOver = false;
		mWinner = null;
	}

	public void reset() {
		clearBoard();
		mCurrentPlayer = 0;
		broadcastUpdate();
	}

	public int registerPlayer(RemotePlayer remotePlayer) {
		// TODO check if full...
		mPlayers.add(remotePlayer);
		return mPlayers.indexOf(remotePlayer);
	}
	
	public void registerObserver(RemotePlayer remotePlayer) {
		mObservers.add(remotePlayer);
	}
	
}
